﻿using System;
using System.Collections.Generic;
using System.Net;
using System.Runtime.ConstrainedExecution;
using System.Text.Json;
using LightCAD.MathLib;

namespace LightCAD.Core.Elements
{
    public class LcArc : LcCurve2d
    {
        public Vector2 Center { get => this.Arc.Center; set => this.Arc.Center = value; }
        public double Radius { get => this.Arc.Radius; set => this.Arc.Radius = value; }
        public double StartAngle { get => this.Arc.StartAngle; set => this.Arc.StartAngle = value; }
        public double EndAngle { get => this.Arc.EndAngle; set => this.Arc.EndAngle = value; }
        public double MoveAngle;
        public Vector2 Startp;
        public Vector2 Endp;
        public Vector2 Midp;
        public Vector2 _center;
        public double ArcLength;

        public Arc2d Arc => this.Curve as Arc2d;
        public LcArc()
        {
            this.Type = BuiltinElementType.Arc;
            this.Curve = new Arc2d();
        }
        public LcArc(Vector2 center, double radius, double startAngle, double endAngle) : this()
        {
            this.Center = center;
            Radius = radius;
            this.StartAngle = startAngle;
            this.EndAngle = endAngle;
        }
        public LcArc(double centerX, double centerY, double radius, double startAngle, double endAngle)
        {
            this.Center.X = centerX;
            this.Center.Y = centerY;
            Radius = radius;
            this.StartAngle = startAngle;
            this.EndAngle = endAngle;
        }

        public void LoadArcStatsProperty()
        {
            //判断是圆心画弧还是三点画弧
            if(_center==null)   //如果圆心等于空  就代表不用圆心画弧
            {
                this.Center = GetCenter(Startp, Midp, Endp);
            }
            else
            {
                this.Center = _center;
            }

                this.Radius = Math.Sqrt(Math.Pow(Center.X - Endp.X, 2) + Math.Pow(Center.Y - Endp.Y, 2)); //计算半径

                this.StartAngle = ThreePointGetAngle(new Vector2(Center.X + 100, Center.Y), Center, this.Startp, true);
                this.EndAngle = ThreePointGetAngle(new Vector2(Center.X + 100, Center.Y), Center, this.Endp, true);
                this.MoveAngle = 0;
                var pdangle = ThreePointGetAngle(this.Startp, this.Midp, this.Endp);
                if (pdangle > 180 || (pdangle < 0 && pdangle > -180))
                {
                    if (this.StartAngle > 0)
                    {
                        this.MoveAngle = 360 - this.StartAngle + this.EndAngle;
                    }
                    else
                    {
                        this.MoveAngle = 360 + this.EndAngle - this.StartAngle;
                    }
                }

                  else
                {
                    if (this.StartAngle > 0)
                    {
                        this.MoveAngle = -this.StartAngle + this.EndAngle - 360;
                    }
                    else
                    {
                        this.MoveAngle = -(360 + this.StartAngle) + this.EndAngle;
                    }
                }

                if (this.MoveAngle < -360)
                {
                    this.MoveAngle = this.MoveAngle + 360;
                }
                if (this.MoveAngle > 360)
                {
                    this.MoveAngle = this.MoveAngle - 360;
                }

                var a = Startp - Center;
                var b = Endp - Center;
                var m = a + b;
                m.Normalize();
                m = m * Radius;
                this.Midp = Center + m;
                if (Math.Abs(this.MoveAngle) > 180)
                {
                    this.Midp = Vector2.Rotate(this.Midp, this.Center, 180);
                }
                ArcLength = MoveAngle * 2 * Math.PI * Radius / 360;
            
        }


        public static Vector2 GetCenter(Vector2 startp,Vector2 midp,Vector2 endp)
        {
            Vector2 Center = new Vector2();
            double a = startp.X - midp.X;
            double b = startp.Y - midp.Y;
            double c = startp.X - endp.X;
            double d = startp.Y - endp.Y;
            double e = ((startp.X * startp.X - midp.X * midp.X) - (midp.Y * midp.Y - startp.Y * startp.Y)) / 2;
            double f = ((startp.X * startp.X - endp.X * endp.X) - (endp.Y * endp.Y - startp.Y * startp.Y)) / 2;

            // 圆心位置 
            var x = (e * d - b * f) / (a * d - b * c);
            var y = (a * f - e * c) / (a * d - b * c);
            Center.X = x;
            Center.Y = y;
            return Center;
        }

        /// <summary>
        /// 三点确定角度
        /// </summary>
        /// <param name="startpoint"></param>
        /// <param name="centerpoint"></param>
        /// <param name="endpoint"></param>
        /// <param name="reversal">是否开启顺时针180为正，逆时针180为负,默认为逆时针负角度</param>
        /// <returns></returns>
        public double ThreePointGetAngle(Vector2 startpoint, Vector2 centerpoint, Vector2 endpoint, bool reversal = false)
        {
            double angle = 0;
            var so = new Vector2(startpoint.X - centerpoint.X, startpoint.Y - centerpoint.Y);
            var eo = new Vector2(endpoint.X - centerpoint.X, endpoint.Y - centerpoint.Y);
            var pddir = (so.X * eo.Y) - (so.Y * eo.X);
            angle = (so.Angle() - eo.Angle()) / Math.PI * 180;
            if (reversal)
            {
                angle = (so.Angle() + (pddir < 0 ? (2 * Math.PI - eo.Angle()) : -eo.Angle())) / Math.PI * 180;
            }

            return angle;
        }

        public override Box2 GetBoundingBox()
        {
            //double cMaxX = this.Center.X + Radius;
            //double cMinX = this.Center.X - Radius;
            //double cMaxY = this.Center.Y + Radius;
            //double cMinY = this.Center.Y - Radius;

            //return new Box2().SetFromPoints(new Vector2(cMinX, cMinY), new Vector2(cMaxX, cMaxY));
            return new Box2().SetFromPoints(this.Startp, this.Endp);
        }
        public override Box2 GetBoundingBox(Matrix3 matrix)
        {

            return new Box2().SetFromPoints(matrix.MultiplyPoint(this.Startp), matrix.MultiplyPoint(this.Endp));
        }

        public override bool IntersectWithBox(Polygon2d testPoly, List<RefChildElement> intersectChildren = null)
        {
            var thisBox = this.BoundingBox;
            if (!thisBox.IntersectsBox(testPoly.BoundingBox))
            {
                //如果元素盒子，与多边形盒子不相交，那就可能不相交
                return false;
            }
            return GeoUtils.IsPolygonIntersectLine(testPoly.Points, this.Startp, this.Endp)
                || GeoUtils.IsPolygonContainsLine(testPoly.Points, this.Startp, this.Endp);
        }

        public override bool IncludedByBox(Polygon2d testPoly, List<RefChildElement> includedChildren = null)
        {
            var thisBox = this.BoundingBox;
            if (!thisBox.IntersectsBox(testPoly.BoundingBox))
            {
                return false;
            }
            return GeoUtils.IsPolygonContainsLine(testPoly.Points, this.Startp, this.Endp);
        }

        public override LcElement Clone()
        {
            var clone = new LcArc();
            clone.Copy(this);
            return clone;
        }

        public override void Copy(LcElement src)
        {
            var arc = ((LcArc)src);
            this.Center = arc.Center;
            this.Radius = arc.Radius;
            this.StartAngle = arc.StartAngle;
            this.EndAngle = arc.EndAngle;
        }

        public void Set(Vector2 center = null, Vector2 start = null, Vector2 mid = null, Vector2 end = null, bool fireChangedEvent = false)
        {
            if (!fireChangedEvent)
            {
                if (start != null) this.Startp = start;
                if (end != null) this.Endp = end;
                if (mid != null) this.Midp = mid;
                if (center != null) {
                    var oldcenter = Center;
                    var newcenter = center;
                    Vector2 movevec = newcenter - oldcenter;
                    this.Center = center;
                    this.Startp += movevec;
                    this.Endp += movevec;
                    this.Midp += movevec;
                }
            }
            else
            {
                bool chg_start = (start != null && start != this.Startp);
                if (chg_start)
                {
                    OnPropertyChangedBefore(nameof(Startp), this.Startp, start);
                    var oldValue = this.Startp;
                    this.Startp = start;
                    OnPropertyChangedAfter(nameof(Startp), oldValue, this.Startp);
                }
                bool chg_end = (end != null && end != this.Endp);
                if (chg_end)
                {
                    OnPropertyChangedBefore(nameof(Endp), this.Endp, end);
                    var oldValue = this.Endp;
                    this.Endp = end;
                    OnPropertyChangedAfter(nameof(Endp), oldValue, this.Endp);
                }
                bool chg_mid = (mid != null && mid != this.Midp);
                if (chg_mid)
                {
                    OnPropertyChangedBefore(nameof(Midp), this.Midp, mid);
                    var oldValue = this.Midp;
                    this.Midp = mid;
                    OnPropertyChangedAfter(nameof(Midp), oldValue, this.Midp);
                }
                bool chg_center = (center != null && center != this.Center);
                if (chg_center)
                {
                    var oldcenter = this.Center;
                    var newcenter = center;
                    //移动向量
                    Vector2 movevec = newcenter - oldcenter;
                    OnPropertyChangedBefore(nameof(Center), this.Center, center);
                    var centeroldValue = this.Center;
                    this.Center = center;
                    OnPropertyChangedAfter(nameof(Center), centeroldValue, this.Center);

                    OnPropertyChangedBefore(nameof(Startp), this.Startp, start + movevec);
                    var startoldValue = this.Startp;
                    this.Startp = start + movevec;
                    OnPropertyChangedAfter(nameof(Startp), startoldValue, this.Startp);

                    OnPropertyChangedBefore(nameof(Endp), this.Endp, end+ movevec);
                    var endoldValue = this.Endp;
                    this.Endp = end+ movevec;
                    OnPropertyChangedAfter(nameof(Endp), endoldValue, this.Endp);

                    OnPropertyChangedBefore(nameof(Midp), this.Midp, mid+ movevec);
                    var midoldValue = this.Midp;
                    this.Midp = mid+ movevec;
                    OnPropertyChangedAfter(nameof(Midp), midoldValue, this.Midp);
                }
            }
        }

        public override void WriteProperties(Utf8JsonWriter writer, JsonSerializerOptions soptions)
        {
            base.WriteProperties(writer, soptions);
            writer.WriteVector2dProperty(nameof(this.Center), this.Center);
            writer.WriteNumberProperty(nameof(this.Radius), this.Radius);
            writer.WriteNumberProperty(nameof(this.StartAngle), this.StartAngle);
            writer.WriteNumberProperty(nameof(this.EndAngle), this.EndAngle);
            writer.WriteNumberProperty(nameof(this.MoveAngle), this.MoveAngle);
            writer.WriteVector2dProperty(nameof(this.Startp), this.Startp);
            writer.WriteVector2dProperty(nameof(this.Endp), this.Endp);
            writer.WriteVector2dProperty(nameof(this.Midp), this.Midp);
            writer.WriteNumberProperty(nameof(this.ArcLength), this.ArcLength);
        }


        public override void ReadProperties(ref JsonElement jele)
        {
            base.ReadProperties(ref jele);
            this.Center = jele.ReadVector2dProperty(nameof(this.Center));
            this.Radius = jele.ReadDoubleProperty(nameof(this.Radius));
            this.StartAngle = jele.ReadDoubleProperty(nameof(this.StartAngle));
            this.EndAngle = jele.ReadDoubleProperty(nameof(this.EndAngle));
            this.MoveAngle = jele.ReadDoubleProperty(nameof(this.MoveAngle));
            this.Startp = jele.ReadVector2dProperty(nameof(this.Startp));
            this.Endp = jele.ReadVector2dProperty(nameof(this.Endp));
            this.Midp = jele.ReadVector2dProperty(nameof(this.Midp));
            this.ArcLength = jele.ReadDoubleProperty(nameof(this.ArcLength));
        }
    }
}
